/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.modules.apisupport;
import java.lang.reflect.Modifier;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import javax.swing.SwingUtilities;
import org.openide.TopManager;
import org.openide.cookies.SourceCookie;
import org.openide.cookies.InstanceCookie;
import org.openide.execution.NbfsURLConnection;
import org.openide.filesystems.*;
import org.openide.nodes.Node;
import org.openide.src.*;
import org.openide.util.*;
import org.openide.util.actions.*;
import org.openide.windows.TopComponent;
public class ShowAPIJavadocAction extends CookieAction {
private static final long serialVersionUID =-2438079845105114834L;
public String getName () {
if (lastCalced == null) {
return "Show API Javadoc";
} else {
String name = lastCalced;
int idx = name.lastIndexOf ((int) '.');
if (idx != -1) name = name.substring (idx + 1);
return "Javadoc: " + name.replace ('$', '.');
}
}
protected String iconResource () {
return "/org/netbeans/modules/apisupport/resources/ShowAPIJavadocIcon.gif";
}
public HelpCtx getHelpCtx () {
return new HelpCtx ("org.netbeans.modules.apisupport.utils");
}
protected Class[] cookieClasses () {
return new Class[] { SourceCookie.class, ClassElement.class, MemberElement.class, InstanceCookie.class };
}
protected int mode () {
return MODE_EXACTLY_ONE;
}
private static int sequence = 0;
private static Node lastCalcedNode = null;
private static String lastCalced = null;
protected boolean enable (Node[] nodes) {
if (! super.enable (nodes)) return false;
final int currentSequence = ++sequence;
//System.err.println("sequence=" + sequence);
final Node n = nodes[0];
RequestProcessor.postRequest (new Runnable () {
public void run () {
if (sequence > currentSequence) {
//System.err.println("Preempted before even doing check");
return;
}
String classname = findClass (n);
if (sequence > currentSequence) {
//System.err.println("Ran out after checking");
return;
}
//System.err.println("setting enabled; currentSequence=" + currentSequence);
setEnabled (classname != null);
lastCalcedNode = n;
lastCalced = classname;
firePropertyChange ("name", null, null);
}
}, 500);
return false;
}
// 1.2 Javac bug:
protected void firePropertyChange (String name, Object old, Object nue) {
super.firePropertyChange (name, old, nue);
}
protected void performAction (Node[] nodes) {
if (! super.enable (nodes)) {
System.err.println("performed when not enabled");
return;
}
Node n = nodes[0];
String classname;
if (n == lastCalcedNode) {
//System.err.println("using last-calculated");
classname = lastCalced;
} else {
//System.err.println("recalcing");
classname = findClass (n);
}
if (classname == null) {
System.err.println("enabled, but now cannot find it");
return;
}
String resource = classname.replace ('.', '/').replace ('$', '.') + ".html";
FileObject fo = FileSystemCapability.DOC.findResource (resource);
if (fo == null) fo = FileSystemCapability.DOC.findResource ("api/" + resource);
if (fo == null) {
System.err.println ("Could not find docs for " + resource +
" in Documentation Repository!");
return;
}
try {
URL url = NbfsURLConnection.encodeFileObject (fo);
TopManager.getDefault ().showUrl (url);
} catch (FileStateInvalidException fsie) {
fsie.printStackTrace ();
}
}
private static String findClass (Node n) {
ClassElement clazz = (ClassElement) n.getCookie (ClassElement.class);
if (clazz == null) {
//System.err.println("no ClassElement, trying MemberElement...");
MemberElement mem = (MemberElement) n.getCookie (MemberElement.class);
if (mem != null) {
//System.err.println("got MemberElement");
clazz = mem.getDeclaringClass ();
}
}
if (clazz == null) {
//System.err.println("neither, trying SourceCookie...");
SourceCookie source = (SourceCookie) n.getCookie (SourceCookie.class);
if (source == null) {
//System.err.println("No SourceCookie, trying InstanceCookie");
InstanceCookie inst = (InstanceCookie) n.getCookie (InstanceCookie.class);
if (inst == null) {
//System.err.println("No InstanceCookie");
return null;
}
try {
clazz = ClassElement.forClass (inst.instanceClass ());
if (clazz == null) {
//System.err.println("Instance class not found");
return null;
}
} catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace ();
return null;
} catch (IOException ioe) {
ioe.printStackTrace ();
return null;
}
} else {
SourceElement element = source.getSource ();
switch (element.getStatus ()) {
case SourceElement.STATUS_ERROR:
// fallthrough
case SourceElement.STATUS_PARTIAL:
//System.err.println("bad parse, ignoring");
return null;
case SourceElement.STATUS_NOT:
/*
System.err.println("partial parse, will finish and then try again");
element.prepare ().addTaskListener (new TaskListener () {
public void taskFinished (Task ignore) {
SwingUtilities.invokeLater (new Runnable () {
public void run () {
ShowAPIJavadocAction action = (ShowAPIJavadocAction) SystemAction.get (ShowAPIJavadocAction.class);
action.setEnabled (action.enable (TopComponent.getRegistry ().getActivatedNodes ()));
}
});
}
});
return null;
*/
// fallthrough
case SourceElement.STATUS_OK:
//System.err.println("full parse (or delayed)");
if (element.getClasses ().length == 0) {
//System.err.println("\tno classes found");
return null;
}
// [PENDING] ought to check specifically for main public one
clazz = element.getClasses ()[0];
break;
default:
throw new InternalError ("illegal parsing state for SourceElement: " + element.getStatus ());
}
}
}
Type type = testCache (Type.createClass (clazz.getName ()));
if (type == null) {
//System.err.println("no found type");
return null;
}
ClassElement clazz2 = ClassElement.forName (type.getFullString ());
if (clazz2 == null) {
System.err.println("could not find clazz for " + type.getFullString ());
return null;
}
SourceElement source = clazz2.getSource ();
if (source == null) {
System.err.println("clazz " + clazz2.getName () + " had no source");
return null;
}
String pkg = source.getPackage ().getFullName ();
if (pkg == null) {
System.err.println("pkg==null");
return null;
}
if (! pkg.equals ("")) pkg += '.';
String classname = clazz2.getName ().getName ();
while ((clazz2 = clazz2.getDeclaringClass ()) != null) {
classname = clazz2.getName ().getName () + '$' + classname;
}
return pkg + classname;
}
// [PENDING] Using a weak map would seem to make sense, but for some reason it
// seems that storing somekey -> null in the weak map "sticks" temporarily, but
// is erased a few seconds later...even while regular keys stay around as expected.
// This does not seem to have anything to do with garbage collection. Using
// a regular hash map all works well, so this (hopefully minor) memory leak is
// reasonable given the likely performance benefit.
private static Map cache = Collections.synchronizedMap (new /*Weak*/HashMap ());
private static Type testCache (Type type) {
if (cache.containsKey (type)) {
Type hit = (Type) cache.get (type);
//System.err.println("Cache hit: " + type + " -> " + hit);
return hit;
}
//System.err.println("Cache miss: " + type);
cache.put (type, null);
Type toRet = test (type);
cache.put (type, toRet);
//System.err.println("Cache add: " + type + " -> " + toRet);
//System.err.println("\tfound: " + cache.containsKey (type) + " " + cache.get (type));
return toRet;
}
private static Type test (Type type) {
//System.err.println("testing: " + type);
if (type == null) {
//System.err.println("\twas null anyway");
return null;
}
if (type.isArray ()) {
//System.err.println("\ttrying element type");
return testCache (type.getElementType ());
}
if (type.isPrimitive ()) {
//System.err.println("\twas primitive");
return null;
}
ClassElement clazz = ClassElement.forName (type.getFullString ());
if (clazz == null) {
// Due to a parser bug, this will actually happen sometimes,
// though it shouldn't: method-local inner classes get here.
//System.err.println("No such clazz");
return null;
}
//System.err.println("Got clazz: " + clazz.getName ());
if ((Modifier.isPublic (clazz.getModifiers ()) || Modifier.isProtected (clazz.getModifiers ()))
&& clazz.getName ().getFullName ().startsWith ("org.openide.")) {
//System.err.println("\tsucceeded");
return Type.createClass (clazz.getName ());
} else {
Identifier superid = clazz.getSuperclass ();
//System.err.println("\ttrying on superclass " + superid);
Type supe = superid == null ? null : testCache (Type.createClass (superid));
if (supe != null) {
//System.err.println("\tsucceeded with superclass");
return supe;
} else {
//System.err.println("\ttrying with interfaces");
Identifier[] xfaces = clazz.getInterfaces ();
for (int i = 0; i < xfaces.length; i++) {
Type xface = testCache (Type.createClass (xfaces[i]));
if (xface != null) return xface;
}
//System.err.println("\tno luck with interfaces");
return null;
}
}
}
}
/*
* Log
* 18 Gandalf-post-FCS1.10.1.6 4/3/00 Jesse Glick Preventing recursion.
* 17 Gandalf-post-FCS1.10.1.5 4/3/00 Jesse Glick Keeping a cache of
* analysis results.
* 16 Gandalf-post-FCS1.10.1.4 3/30/00 Jesse Glick
* 15 Gandalf-post-FCS1.10.1.3 3/30/00 Jesse Glick Using InstanceCookie as
* a last resort.
* 14 Gandalf-post-FCS1.10.1.2 3/28/00 Jesse Glick Slowing down javadoc
* action to avoid loading down the system.
* 13 Gandalf-post-FCS1.10.1.1 3/28/00 Jesse Glick 1.2 compiler bug
* workaround.
* 12 Gandalf-post-FCS1.10.1.0 3/28/00 Jesse Glick Rewritten to use a
* source parse rather than loading the class.
* 11 Gandalf 1.10 1/24/00 Jesse Glick #3964 fixed.
* 10 Gandalf 1.9 1/11/00 Jesse Glick TopComponent template.
* New icons for CompilerTypeTester and ShowAPIJavadocAction.
* 9 Gandalf 1.8 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 8 Gandalf 1.7 10/7/99 Jesse Glick Inexplicable compile
* errors--org.openide.nodes.Node import does not work.
* 7 Gandalf 1.6 10/6/99 Jesse Glick Added table of contents,
* anchored context help.
* 6 Gandalf 1.5 10/5/99 Jesse Glick Will have API docs in an
* NBM.
* 5 Gandalf 1.4 9/30/99 Jesse Glick Package rename and misc.
* 4 Gandalf 1.3 9/24/99 Jesse Glick Temporarily suppressing
* exception.
* 3 Gandalf 1.2 9/21/99 Jesse Glick Small bugfixes.
* 2 Gandalf 1.1 9/14/99 Jesse Glick Context help.
* 1 Gandalf 1.0 9/12/99 Jesse Glick
* $
*/